package cn.snmp.mutual.adapter;

import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

import cn.snmp.mutual.nms.SnmpOID;
import lombok.extern.slf4j.Slf4j;
import org.snmp4j.MessageException;
import org.snmp4j.PDU;
import org.snmp4j.ScopedPDU;
import org.snmp4j.Snmp;
import org.snmp4j.Target;
import org.snmp4j.event.ResponseEvent;
import org.snmp4j.mp.MPv3;
import org.snmp4j.mp.SnmpConstants;
import org.snmp4j.security.SecurityModels;
import org.snmp4j.security.SecurityProtocols;
import org.snmp4j.security.USM;
import org.snmp4j.smi.OID;
import org.snmp4j.smi.OctetString;
import org.snmp4j.smi.VariableBinding;
import org.snmp4j.transport.DefaultUdpTransportMapping;
import cn.snmp.mutual.common.SetInfoHandler;
import cn.snmp.mutual.common.SnmpDispatcher;
import cn.snmp.mutual.common.SnmpGetHandler;
import cn.snmp.mutual.data.ObjService;
import cn.snmp.mutual.common.snmp.SnmpNioTarget;
import cn.snmp.mutual.util.MibNodeDataUtils;

/**
 * @author dengkn
 * SnmpAdapter 用户使用浅层入口类
 */
@Slf4j
public class SnmpAdapter {

    private static SnmpOID usmStatsNotInTimeWindows = new SnmpOID(".1.3.6.1.6.3.15.1.1.2.0");
    private static SnmpOID usmStatsUnknownUserNames = new SnmpOID(".1.3.6.1.6.3.15.1.1.3.0");
    private static SnmpOID usmStatsWrongDigests = new SnmpOID(".1.3.6.1.6.3.15.1.1.5.0");
    private static SnmpOID sysObjectID = new SnmpOID(".1.3.6.1.2.1.1.2.0");
    private static final String PING_MIB_OID = ".1.3.6.1.2.1.1.2.0";
    private static Map<Integer, Integer> neStepIdCache = new HashMap<Integer, Integer>();
    private static final int SYMBOL_SUCESS = 0;
    private static final int SYMBO_USER_NAME_ERROR = 1;
    private static final int SYMBO_AUTH_ERROR = 2;
    private static final int SYMBO_PRIVACY_ERROR = 3;
    private static final int SYMBO_TIME_WINDOW = 4;
    private static final int SYMBO_UNKNOW_ERROR = 5;
    private static SnmpDispatcher snmpDispatcherIpv4 = SnmpDispatcher.snmpDispatcher;
    private static SnmpDispatcher snmpDispatcherIpv6 = SnmpDispatcher.snmpDispatcherIpv6;
    private static int stepId;
    private static int neId;


    public ObjService setInfo(ObjService param) {
        SnmpNioTarget snmpNioTarget = new SetInfoHandler().hander(MibNodeDataUtils.getMibTable(param));
        snmpNioTarget.setTargetHost((String) param.getValue("IPAddress"));
        snmpNioTarget.setSNMPOpType(5);
        //snmpNioTarget.setRetries(0);
        initResultError(snmpNioTarget);
        try {
            send((String) param.getValue("IPADDRESS"), getSnmpVion(param), snmpNioTarget);
        } catch (Exception e) {
            log.error("Snmp send error! ", e);
        }
        return snmpNioTarget.getResult();
    }

    private int getSnmpVion(ObjService data) {
        int snmpVersion = 2;
        String snmpVersionStr = (String) data.getValue("snmpVersion");
        if (snmpVersionStr != null || "".equals(snmpVersionStr)) {
            try {
                snmpVersion = Integer.parseInt(snmpVersionStr);
            } catch (Exception e) {
                log.error("mutual version typr transformation error!", e);
            }
        }
        return snmpVersion;
    }

    public ObjService getInfo(ObjService param) {
        //snmpDispatcherIpv4 = SnmpDispatcher.snmpDispatcher;
        //snmpDispatcherIpv6 = SnmpDispatcher.snmpDispatcherIpv6;
        SnmpGetHandler snmpGetHandler = new SnmpGetHandler();
        SnmpNioTarget snmpNioTarget = snmpGetHandler.snmpGetHander(param);
        initResultError(snmpNioTarget);
        try {
            send((String) param.getValue("IPADDRESS"), getSnmpVion(param), snmpNioTarget);
        } catch (MessageException e) {
            log.error("Snmp send error! ", e);
        }
        return snmpNioTarget.getResult();
    }

    private static void initResultError(SnmpNioTarget target) {
        ObjService result = target.getResult();
        result.setValue("ErrCode", "-2");
        result.setValue("ErrDesc", "operator error");
    }

    private void send(String ipAddress, int snmpVersion, SnmpNioTarget snmpNioTarget) throws MessageException {
        try {
            if (snmpVersion == SnmpConstants.version3) {
                snmpV3Send(ipAddress, snmpNioTarget);
            } else {
                if (ipAddress.contains(":")) {
                    snmpDispatcherIpv6.sendPdu(snmpNioTarget);
                } else {
                    snmpDispatcherIpv4.sendPdu(snmpNioTarget);
                }
            }
        } catch (MessageException e) {
            log.error("Snmp send error! ", e);
        } /*finally {
			try {
				if (snmpDispatcherIpv4.getSnmp() != null) {
					System.out.println("close_xxxxxxxxxx");
					snmpDispatcherIpv4.getSnmp().close();
				}
			} catch (IOException e) {
				logger.error("Snmp close error! ", e);
			}
			try {
				if (snmpDispatcherIpv6.getSnmp() != null) {
					System.out.println("close_xxxxxxxxxx");
					snmpDispatcherIpv6.getSnmp().close();
				}
			} catch (IOException e) {
				logger.error("Snmp close error! ", e);
			}
		}*/
    }

    private void snmpV3Send(String ipAddress, SnmpNioTarget snmpNioTarget) {
        int pingResult;
        if (neStepIdCache.get(neId) == null) {
            neStepIdCache.put(neId, stepId);
            pingResult = snmpPing(snmpNioTarget);
        } else {
            if (neStepIdCache.get(neId) != stepId) {
                neStepIdCache.put(neId, stepId);
                pingResult = snmpPing(snmpNioTarget);
            } else {
                pingResult = SYMBOL_SUCESS;
            }
        }
        try {
            if (pingResult == SYMBOL_SUCESS) {
                if (ipAddress.contains(":")) {
                    snmpDispatcherIpv6.sendPdu(snmpNioTarget);
                } else {
                    snmpDispatcherIpv4.sendPdu(snmpNioTarget);
                }
            }
        } catch (MessageException e) {
            log.error("Snmp v3 send error! ", e);
        }
    }

    private int snmpPing(SnmpNioTarget snmpNioTarget) {
        PDU pdu = new ScopedPDU();
        pdu.setType(PDU.GET);
        pdu.add(new VariableBinding(new OID(PING_MIB_OID)));
        Snmp snmp = null;
        ResponseEvent response = null;
        try {
            if (snmp == null) {
                USM usm = new USM(SecurityProtocols.getInstance(), new OctetString(MPv3.createLocalEngineID()), 0);
                SecurityModels.getInstance().addSecurityModel(usm);
                snmp = new Snmp(new DefaultUdpTransportMapping());
                snmp.listen();
                Target target = snmpNioTarget.getTarget(snmpNioTarget);
                snmp.getUSM().addUser(snmpNioTarget.getUsmUser());
                response = snmp.send(pdu, target);
                log.debug("Synchronize message from :  " + response.getPeerAddress());
                log.debug("request :  " + response.getRequest());
                log.debug("response :  " + response.getResponse());
            }
        } catch (IOException e) {
            log.error("Snmp v3 ping error! ", e);
        } finally {
            try {
                if (snmp != null) {
                    snmp.close();
                }
            } catch (IOException e) {
                log.error("Snmp v3 ping close error! ", e);
            }
        }
        if (response.getResponse() != null) {
            SnmpOID oid = new SnmpOID(response.getResponse().getVariableBindings().get(0).getOid().getValue());
            if (oid.equals(usmStatsNotInTimeWindows)) {
                snmp.discoverAuthoritativeEngineID(snmpNioTarget.getTarget().getAddress(), 100);
                return SYMBO_TIME_WINDOW;
            } else if (oid.equals(usmStatsWrongDigests)) {
                return SYMBO_AUTH_ERROR;
            } else if (oid.equals(usmStatsUnknownUserNames)) {
                return SYMBO_USER_NAME_ERROR;
            } else if (oid.equals(sysObjectID)) {
                return SYMBOL_SUCESS;
            } else {
                return SYMBO_UNKNOW_ERROR;
            }
        } else {
            return SYMBO_PRIVACY_ERROR;
        }
    }
}
